/**
 ******************************************************************************
 * @file           : bsp_rtc.c
 * @brief          : RTC Driver Implementation (STM32F1xx)
 ******************************************************************************
 * @attention
 *
 * RTC驱动程序实现文件
 * 提供实时时钟功能
 * 支持日期时间读写、时间戳转换等
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/RTC/bsp_rtc.h"
#include "./BSP/RTC/bsp_rtc_config.h"

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
RTC_HandleTypeDef sg_rtc_handle;                       /*!< RTC句柄 */
static volatile uint8_t sg_rtc_initialized = 0;        /*!< 初始化标志 */

/* 每月天数表(平年) */
static const uint8_t sg_month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

/* Private function prototypes -----------------------------------------------*/
static void rtc_msp_init(void);
static uint8_t rtc_is_datetime_valid(const RTC_DateTime_t *datetime);
static void rtc_hal_to_datetime(RTC_TimeTypeDef *time, RTC_DateTypeDef *date, RTC_DateTime_t *datetime);
static void rtc_datetime_to_hal(const RTC_DateTime_t *datetime, RTC_TimeTypeDef *time, RTC_DateTypeDef *date);

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       RTC底层初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置RTC时钟源
 */
static void rtc_msp_init(void)
{
    RCC_OscInitTypeDef osc_init = {0};
    RCC_PeriphCLKInitTypeDef periph_clk_init = {0};

    /* 使能PWR时钟 */
    __HAL_RCC_PWR_CLK_ENABLE();

    /* 使能备份域访问 */
    HAL_PWR_EnableBkUpAccess();

#if RTC_CLOCK_SOURCE_LSE
    /* 配置LSE */
    osc_init.OscillatorType = RCC_OSCILLATORTYPE_LSE;
    osc_init.LSEState = RCC_LSE_ON;
    osc_init.PLL.PLLState = RCC_PLL_NONE;

    if (HAL_RCC_OscConfig(&osc_init) != HAL_OK)
    {
        log_e("LSE配置失败");
        return;
    }

    /* 选择LSE作为RTC时钟源 */
    periph_clk_init.PeriphClockSelection = RCC_PERIPHCLK_RTC;
    periph_clk_init.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
#else
    /* 配置LSI */
    osc_init.OscillatorType = RCC_OSCILLATORTYPE_LSI;
    osc_init.LSIState = RCC_LSI_ON;
    osc_init.PLL.PLLState = RCC_PLL_NONE;

    if (HAL_RCC_OscConfig(&osc_init) != HAL_OK)
    {
        log_e("LSI配置失败");
        return;
    }

    /* 选择LSI作为RTC时钟源 */
    periph_clk_init.PeriphClockSelection = RCC_PERIPHCLK_RTC;
    periph_clk_init.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
#endif

    if (HAL_RCCEx_PeriphCLKConfig(&periph_clk_init) != HAL_OK)
    {
        log_e("RTC时钟源配置失败");
        return;
    }

    /* 使能RTC时钟 */
    __HAL_RCC_RTC_ENABLE();
}

/**
 * @brief       检查日期时间有效性
 *
 * @param[in]   datetime 日期时间结构体指针
 *
 * @retval      1 有效
 * @retval      0 无效
 *
 * @note        检查年月日时分秒的合法性
 */
static uint8_t rtc_is_datetime_valid(const RTC_DateTime_t *datetime)
{
    uint8_t max_day;

    if (datetime == NULL)
    {
        return 0;
    }

    /* 检查年份 */
    if (datetime->year < 2000 || datetime->year > 2099)
    {
        return 0;
    }

    /* 检查月份 */
    if (datetime->month < 1 || datetime->month > 12)
    {
        return 0;
    }

    /* 检查日期 */
    max_day = sg_month_days[datetime->month - 1];
    if (datetime->month == 2 && rtc_is_leap_year(datetime->year))
    {
        max_day = 29;
    }
    if (datetime->day < 1 || datetime->day > max_day)
    {
        return 0;
    }

    /* 检查时间 */
    if (datetime->hour > 23 || datetime->minute > 59 || datetime->second > 59)
    {
        return 0;
    }

    return 1;
}

/**
 * @brief       HAL结构转为DateTime结构
 *
 * @param[in]   time     HAL时间结构
 * @param[in]   date     HAL日期结构
 * @param[out]  datetime DateTime结构
 *
 * @retval      无
 *
 * @note        内部转换函数
 */
static void rtc_hal_to_datetime(RTC_TimeTypeDef *time, RTC_DateTypeDef *date, RTC_DateTime_t *datetime)
{
    datetime->year = 2000 + date->Year;
    datetime->month = date->Month;
    datetime->day = date->Date;
    datetime->hour = time->Hours;
    datetime->minute = time->Minutes;
    datetime->second = time->Seconds;
    datetime->week = date->WeekDay;
}

/**
 * @brief       DateTime结构转为HAL结构
 *
 * @param[in]   datetime DateTime结构
 * @param[out]  time     HAL时间结构
 * @param[out]  date     HAL日期结构
 *
 * @retval      无
 *
 * @note        内部转换函数
 */
static void rtc_datetime_to_hal(const RTC_DateTime_t *datetime, RTC_TimeTypeDef *time, RTC_DateTypeDef *date)
{
    time->Hours = datetime->hour;
    time->Minutes = datetime->minute;
    time->Seconds = datetime->second;
    time->SubSeconds = 0;
    time->DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    time->StoreOperation = RTC_STOREOPERATION_RESET;

    date->Year = datetime->year - 2000;
    date->Month = datetime->month;
    date->Date = datetime->day;
    date->WeekDay = datetime->week;
}

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       RTC初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        初始化RTC时钟和日期时间
 */
void rtc_init(void)
{
    if (sg_rtc_initialized)
    {
        log_w("RTC已经初始化");
        return;
    }

    /* 底层初始化 */
    rtc_msp_init();

    /* 配置RTC */
    sg_rtc_handle.Instance = RTC;
    sg_rtc_handle.Init.AsynchPrediv = RTC_ASYNCH_PREDIV;
    sg_rtc_handle.Init.SynchPrediv = RTC_SYNCH_PREDIV;
    sg_rtc_handle.Init.OutPut = RTC_OUTPUT_DISABLE;

    if (HAL_RTC_Init(&sg_rtc_handle) != HAL_OK)
    {
        log_e("RTC初始化失败");
        return;
    }

    /* 检查是否首次初始化 */
    if (HAL_RTCEx_BKUPRead(&sg_rtc_handle, RTC_BKP_DR1) != RTC_BKP_FLAG_INITIALIZED)
    {
        /* 首次初始化，设置默认时间：2024-01-01 00:00:00 */
        RTC_DateTime_t default_time = {2024, 1, 1, 0, 0, 0, 1};
        rtc_set_datetime(&default_time);

        /* 写入标志 */
        HAL_RTCEx_BKUPWrite(&sg_rtc_handle, RTC_BKP_DR1, RTC_BKP_FLAG_INITIALIZED);
        
        log_i("RTC首次初始化，设置默认时间");
    }

    sg_rtc_initialized = 1;
    log_i("RTC初始化完成");
}

/**
 * @brief       RTC反初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        释放RTC资源
 */
void rtc_deinit(void)
{
    if (!sg_rtc_initialized)
    {
        return;
    }

    HAL_RTC_DeInit(&sg_rtc_handle);
    sg_rtc_initialized = 0;

    log_i("RTC已反初始化");
}

/**
 * @brief       设置RTC日期时间
 *
 * @param[in]   datetime 日期时间结构体指针
 *
 * @retval      RTC_OK         设置成功
 * @retval      RTC_ERROR_PARAM 参数错误
 * @retval      RTC_ERROR_HAL   HAL库错误
 *
 * @note        同时设置日期和时间
 */
RTC_Error_e rtc_set_datetime(const RTC_DateTime_t *datetime)
{
    RTC_TimeTypeDef time = {0};
    RTC_DateTypeDef date = {0};

    /* 参数检查 */
    if (!rtc_is_datetime_valid(datetime))
    {
        log_e("日期时间参数无效");
        return RTC_ERROR_PARAM;
    }

    if (!sg_rtc_initialized)
    {
        log_e("RTC未初始化");
        return RTC_ERROR_PARAM;
    }

    /* 转换为HAL结构 */
    rtc_datetime_to_hal(datetime, &time, &date);

    /* 设置时间 */
    if (HAL_RTC_SetTime(&sg_rtc_handle, &time, RTC_FORMAT_BIN) != HAL_OK)
    {
        log_e("设置时间失败");
        return RTC_ERROR_HAL;
    }

    /* 设置日期 */
    if (HAL_RTC_SetDate(&sg_rtc_handle, &date, RTC_FORMAT_BIN) != HAL_OK)
    {
        log_e("设置日期失败");
        return RTC_ERROR_HAL;
    }

    log_i("设置日期时间: %04d-%02d-%02d %02d:%02d:%02d", 
          datetime->year, datetime->month, datetime->day,
          datetime->hour, datetime->minute, datetime->second);

    return RTC_OK;
}

/**
 * @brief       设置RTC时间
 *
 * @param[in]   hour   时(0-23)
 * @param[in]   minute 分(0-59)
 * @param[in]   second 秒(0-59)
 *
 * @retval      RTC_OK         设置成功
 * @retval      RTC_ERROR_PARAM 参数错误
 * @retval      RTC_ERROR_HAL   HAL库错误
 *
 * @note        只设置时间，不改变日期
 */
RTC_Error_e rtc_set_time(uint8_t hour, uint8_t minute, uint8_t second)
{
    RTC_TimeTypeDef time = {0};

    /* 参数检查 */
    if (hour > 23 || minute > 59 || second > 59)
    {
        log_e("时间参数无效");
        return RTC_ERROR_PARAM;
    }

    if (!sg_rtc_initialized)
    {
        log_e("RTC未初始化");
        return RTC_ERROR_PARAM;
    }

    /* 配置时间 */
    time.Hours = hour;
    time.Minutes = minute;
    time.Seconds = second;

    if (HAL_RTC_SetTime(&sg_rtc_handle, &time, RTC_FORMAT_BIN) != HAL_OK)
    {
        log_e("设置时间失败");
        return RTC_ERROR_HAL;
    }

    log_d("设置时间: %02d:%02d:%02d", hour, minute, second);
    return RTC_OK;
}

/**
 * @brief       设置RTC日期
 *
 * @param[in]   year  年(2000-2099)
 * @param[in]   month 月(1-12)
 * @param[in]   day   日(1-31)
 *
 * @retval      RTC_OK         设置成功
 * @retval      RTC_ERROR_PARAM 参数错误
 * @retval      RTC_ERROR_HAL   HAL库错误
 *
 * @note        只设置日期，不改变时间
 */
RTC_Error_e rtc_set_date(uint16_t year, uint8_t month, uint8_t day)
{
    RTC_DateTypeDef date = {0};
    uint8_t week;

    /* 参数检查 */
    if (year < 2000 || year > 2099 || month < 1 || month > 12)
    {
        log_e("日期参数无效");
        return RTC_ERROR_PARAM;
    }

    if (!sg_rtc_initialized)
    {
        log_e("RTC未初始化");
        return RTC_ERROR_PARAM;
    }

    /* 计算星期 */
    week = rtc_get_week(year, month, day);

    /* 配置日期 */
    date.Year = year - 2000;
    date.Month = month;
    date.Date = day;
    date.WeekDay = week;

    if (HAL_RTC_SetDate(&sg_rtc_handle, &date, RTC_FORMAT_BIN) != HAL_OK)
    {
        log_e("设置日期失败");
        return RTC_ERROR_HAL;
    }

    log_d("设置日期: %04d-%02d-%02d", year, month, day);
    return RTC_OK;
}

/**
 * @brief       获取RTC日期时间
 *
 * @param[out]  datetime 日期时间结构体指针
 *
 * @retval      RTC_OK         获取成功
 * @retval      RTC_ERROR_PARAM 参数错误
 * @retval      RTC_ERROR_HAL   HAL库错误
 *
 * @note        同时获取日期和时间
 */
RTC_Error_e rtc_get_datetime(RTC_DateTime_t *datetime)
{
    RTC_TimeTypeDef time = {0};
    RTC_DateTypeDef date = {0};

    /* 参数检查 */
    if (datetime == NULL)
    {
        log_w("datetime指针为NULL");
        return RTC_ERROR_PARAM;
    }

    if (!sg_rtc_initialized)
    {
        log_e("RTC未初始化");
        return RTC_ERROR_PARAM;
    }

    /* 获取时间 */
    if (HAL_RTC_GetTime(&sg_rtc_handle, &time, RTC_FORMAT_BIN) != HAL_OK)
    {
        log_e("获取时间失败");
        return RTC_ERROR_HAL;
    }

    /* 获取日期 */
    if (HAL_RTC_GetDate(&sg_rtc_handle, &date, RTC_FORMAT_BIN) != HAL_OK)
    {
        log_e("获取日期失败");
        return RTC_ERROR_HAL;
    }

    /* 转换为DateTime结构 */
    rtc_hal_to_datetime(&time, &date, datetime);

    return RTC_OK;
}

/**
 * @brief       获取RTC时间
 *
 * @param[out]  hour   时
 * @param[out]  minute 分
 * @param[out]  second 秒
 *
 * @retval      RTC_OK         获取成功
 * @retval      RTC_ERROR_PARAM 参数错误
 * @retval      RTC_ERROR_HAL   HAL库错误
 *
 * @note        只获取时间
 */
RTC_Error_e rtc_get_time(uint8_t *hour, uint8_t *minute, uint8_t *second)
{
    RTC_TimeTypeDef time = {0};

    /* 参数检查 */
    if (hour == NULL || minute == NULL || second == NULL)
    {
        log_w("时间指针为NULL");
        return RTC_ERROR_PARAM;
    }

    if (!sg_rtc_initialized)
    {
        log_e("RTC未初始化");
        return RTC_ERROR_PARAM;
    }

    /* 获取时间 */
    if (HAL_RTC_GetTime(&sg_rtc_handle, &time, RTC_FORMAT_BIN) != HAL_OK)
    {
        log_e("获取时间失败");
        return RTC_ERROR_HAL;
    }

    *hour = time.Hours;
    *minute = time.Minutes;
    *second = time.Seconds;

    return RTC_OK;
}

/**
 * @brief       获取RTC日期
 *
 * @param[out]  year  年
 * @param[out]  month 月
 * @param[out]  day   日
 *
 * @retval      RTC_OK         获取成功
 * @retval      RTC_ERROR_PARAM 参数错误
 * @retval      RTC_ERROR_HAL   HAL库错误
 *
 * @note        只获取日期
 */
RTC_Error_e rtc_get_date(uint16_t *year, uint8_t *month, uint8_t *day)
{
    RTC_DateTypeDef date = {0};

    /* 参数检查 */
    if (year == NULL || month == NULL || day == NULL)
    {
        log_w("日期指针为NULL");
        return RTC_ERROR_PARAM;
    }

    if (!sg_rtc_initialized)
    {
        log_e("RTC未初始化");
        return RTC_ERROR_PARAM;
    }

    /* 获取日期 */
    if (HAL_RTC_GetDate(&sg_rtc_handle, &date, RTC_FORMAT_BIN) != HAL_OK)
    {
        log_e("获取日期失败");
        return RTC_ERROR_HAL;
    }

    *year = 2000 + date.Year;
    *month = date.Month;
    *day = date.Date;

    return RTC_OK;
}

/**
 * @brief       获取当前时间戳
 *
 * @param       无
 *
 * @retval      时间戳(秒)
 *
 * @note        返回从1970-01-01 00:00:00到现在的秒数
 */
uint32_t rtc_get_timestamp(void)
{
    RTC_DateTime_t datetime;
    struct tm timeinfo = {0};

    if (rtc_get_datetime(&datetime) != RTC_OK)
    {
        return 0;
    }

    /* 转换为struct tm */
    timeinfo.tm_year = datetime.year - 1900;
    timeinfo.tm_mon = datetime.month - 1;
    timeinfo.tm_mday = datetime.day;
    timeinfo.tm_hour = datetime.hour;
    timeinfo.tm_min = datetime.minute;
    timeinfo.tm_sec = datetime.second;

    /* 转换为时间戳 */
    return (uint32_t)mktime(&timeinfo);
}

/**
 * @brief       根据时间戳设置RTC
 *
 * @param[in]   timestamp 时间戳(秒)
 *
 * @retval      RTC_OK         设置成功
 * @retval      RTC_ERROR_PARAM 参数错误
 *
 * @note        将时间戳转换为日期时间并设置
 */
RTC_Error_e rtc_set_timestamp(uint32_t timestamp)
{
    struct tm *timeinfo;
    time_t rawtime = (time_t)timestamp;
    RTC_DateTime_t datetime;

    /* 转换时间戳 */
    timeinfo = localtime(&rawtime);
    if (timeinfo == NULL)
    {
        log_e("时间戳转换失败");
        return RTC_ERROR_PARAM;
    }

    /* 填充DateTime结构 */
    datetime.year = 1900 + timeinfo->tm_year;
    datetime.month = 1 + timeinfo->tm_mon;
    datetime.day = timeinfo->tm_mday;
    datetime.hour = timeinfo->tm_hour;
    datetime.minute = timeinfo->tm_min;
    datetime.second = timeinfo->tm_sec;
    datetime.week = timeinfo->tm_wday == 0 ? 7 : timeinfo->tm_wday;

    return rtc_set_datetime(&datetime);
}

/**
 * @brief       计算星期几
 *
 * @param[in]   year  年
 * @param[in]   month 月
 * @param[in]   day   日
 *
 * @retval      星期(1-7, 1=周一)
 *
 * @note        使用蔡勒公式计算
 */
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day)
{
    uint8_t week;

    if (month == 1 || month == 2)
    {
        month += 12;
        year--;
    }

    week = (day + 2 * month + 3 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400) % 7;
    return week == 0 ? 7 : week;
}

/**
 * @brief       判断是否为闰年
 *
 * @param[in]   year 年份
 *
 * @retval      1 闰年
 * @retval      0 平年
 *
 * @note        能被4整除且不能被100整除，或能被400整除
 */
uint8_t rtc_is_leap_year(uint16_t year)
{
    return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
}

/**
 * @brief       打印当前日期时间（用于调试）
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        显示格式化的日期时间
 */
void rtc_print_datetime(void)
{
    RTC_DateTime_t datetime;
    const char *week_str[] = {"", "周一", "周二", "周三", "周四", "周五", "周六", "周日"};

    if (rtc_get_datetime(&datetime) == RTC_OK)
    {
        log_i("当前时间: %04d-%02d-%02d %s %02d:%02d:%02d", 
              datetime.year, datetime.month, datetime.day, week_str[datetime.week],
              datetime.hour, datetime.minute, datetime.second);
    }
}
